Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add redundant type annotations lint #10570

Merged
merged 5 commits into from
Jun 6, 2023

Conversation

AlessioC31
Copy link
Contributor

@AlessioC31 AlessioC31 commented Mar 30, 2023

Hello, I'm trying to add the redundat_type_annotations lint.

It's still WIP but I'd like to start gathering some feedbacks to be sure that I'm not doing things 100% wrong :)

Right now it still misses lints like:

  • let foo: u32 = 5_u32,
  • let foo: String = STest2::func()
  • let foo: String = self.func() (MethodCall)
  • refs
  • Generics

I've some problems regarding the second example above, in the init part of the Local I have:

init: Some(
                Expr {
                    hir_id: HirId(DefId(0:24 ~ playground[e1bd]::main).58),
                    kind: Call(
                        Expr {
                            hir_id: HirId(DefId(0:24 ~ playground[e1bd]::main).59),
                            kind: Path(
                                TypeRelative(
                                    Ty {
                                        hir_id: HirId(DefId(0:24 ~ playground[e1bd]::main).61),
                                        kind: Path(
                                            Resolved(
                                                None,
                                                Path {
                                                    span: src/main.rs:77:21: 77:27 (#0),
                                                    res: Def(
                                                        Struct,
                                                        DefId(0:17 ~ playground[e1bd]::STest2),
                                                    ),
                                                    segments: [
                                                        PathSegment {
                                                            ident: STest2#0,
                                                            hir_id: HirId(DefId(0:24 ~ playground[e1bd]::main).60),
                                                            res: Def(
                                                                Struct,
                                                                DefId(0:17 ~ playground[e1bd]::STest2),
                                                            ),
                                                            args: None,
                                                            infer_args: true,
                                                        },
                                                    ],
                                                },
                                            ),
                                        ),
                                        span: src/main.rs:77:21: 77:27 (#0),
                                    },
                                    PathSegment {
                                        ident: get_numb#0,
                                        hir_id: HirId(DefId(0:24 ~ playground[e1bd]::main).62),
                                        res: Err,
                                        args: None,
                                        infer_args: true,
                                    },
                                ),
                            ),
                            span: src/main.rs:77:21: 77:37 (#0),
                        },
                        [],
                    ),
                    span: src/main.rs:77:21: 77:39 (#0),
                },
            ),

And I'm not sure how to get the return type of the function STest2::func() since the resolved path DefId points to the struct itself and not the function. Do you have any idea on how I could get this information in this case?

Thanks!

changelog: changelog: [redundant_type_annotations]: New lint to warn on redundant type annotations

fixes #9155

@rustbot
Copy link
Collaborator

rustbot commented Mar 30, 2023

Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @xFrednet (or someone else) soon.

Please see the contribution instructions for more information. Namely, in order to ensure the minimum review times lag, PR authors and assigned reviewers should ensure that the review label (S-waiting-on-review and S-waiting-on-author) stays updated, invoking these commands when appropriate:

  • @rustbot author: the review is finished, PR author should check the comments and take action accordingly
  • @rustbot review: the author is ready for a review, this PR will be queued again in the reviewer's queue

@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties label Mar 30, 2023
@AlessioC31 AlessioC31 force-pushed the redundant_type_annotations branch 5 times, most recently from ddc455a to 4770ee1 Compare March 30, 2023 13:53
@bors
Copy link
Collaborator

bors commented Apr 1, 2023

☔ The latest upstream changes (presumably #10534) made this pull request unmergeable. Please resolve the merge conflicts.

Copy link
Member

@xFrednet xFrednet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Welcome to Clippy. Thank you for your patience while waiting on a review!

I've left some review comments. Please don't be discouraged by them, I believe this lint can become quite complex depending on how detailed and how many edge cases we want to catch. Please reach out if you require any further assistance. :)

clippy_lints/src/redundant_type_annotations.rs Outdated Show resolved Hide resolved
clippy_lints/src/redundant_type_annotations.rs Outdated Show resolved Hide resolved
clippy_lints/src/redundant_type_annotations.rs Outdated Show resolved Hide resolved
clippy_lints/src/redundant_type_annotations.rs Outdated Show resolved Hide resolved
tests/ui/redundant_type_annotations.rs Outdated Show resolved Hide resolved
tests/ui/redundant_type_annotations.rs Outdated Show resolved Hide resolved
clippy_lints/src/redundant_type_annotations.rs Outdated Show resolved Hide resolved
@AlessioC31 AlessioC31 force-pushed the redundant_type_annotations branch 2 times, most recently from fffd85b to 5ae56f0 Compare April 4, 2023 21:43
@AlessioC31
Copy link
Contributor Author

Welcome to Clippy. Thank you for your patience while waiting on a review!

I've left some review comments. Please don't be discouraged by them, I believe this lint can become quite complex depending on how detailed and how many edge cases we want to catch. Please reach out if you require any further assistance. :)

Thanks for the review!

@AlessioC31 AlessioC31 force-pushed the redundant_type_annotations branch 8 times, most recently from e858255 to 5977676 Compare April 6, 2023 08:25
@xFrednet
Copy link
Member

xFrednet commented Apr 6, 2023

Thanks for the review!

You're welcome. Are you still working on the implementation or is the branch ready for another review? :)

@AlessioC31
Copy link
Contributor Author

AlessioC31 commented Apr 6, 2023

Thanks for the review!

You're welcome. Are you still working on the implementation or is the branch ready for another review? :)

I'm still working on it. Right now I've problems with Lit type of expressions (like how to map a Lit to a Ty) :)
Do you have any suggestion on where I could have a look?

Or when is a type annotation useful in case of a Lit?

pub enum LitKind {
    /// A string literal (`"foo"`). The symbol is unescaped, and so may differ
    /// from the original token's symbol.
    Str(Symbol, StrStyle),
    /// A byte string (`b"foo"`). Not stored as a symbol because it might be
    /// non-utf8, and symbols only allow utf8 strings.
    ByteStr(Lrc<[u8]>, StrStyle),
    /// A byte char (`b'f'`).
    Byte(u8),
    /// A character literal (`'a'`).
    Char(char),
    /// An integer literal (`1`).
    Int(u128, LitIntType),
    /// A float literal (`1.0`, `1f64` or `1E10f64`). The pre-suffix part is
    /// stored as a symbol rather than `f64` so that `LitKind` can impl `Eq`
    /// and `Hash`.
    Float(Symbol, LitFloatType),
    /// A boolean literal (`true`, `false`).
    Bool(bool),
    /// Placeholder for a literal that wasn't well-formed in some way.
    Err,
}

In almost all these cases we can say that the annotation is redundant, or am I missing anything?
The only exception I can think about is in case of unsuffixed numbers (Int and Float literals)

@AlessioC31 AlessioC31 force-pushed the redundant_type_annotations branch 2 times, most recently from 9b60cbf to a29fd4f Compare April 6, 2023 23:16
@bors
Copy link
Collaborator

bors commented Apr 7, 2023

☔ The latest upstream changes (presumably #10601) made this pull request unmergeable. Please resolve the merge conflicts.

@xFrednet
Copy link
Member

xFrednet commented Apr 7, 2023

I'm still working on it. Right now I've problems with Lit type of expressions (like how to map a Lit to a Ty) :) Do you have any suggestion on where I could have a look?

I don't think there is an easy way to map it. You could request the type of the expression, but that will probably not help too much.

Or when is a type annotation useful in case of a Lit?

A type annotation is useful or even required when the literal is unsuffixed. For example 8 can be several types, the compiler would then look at its usages to determine the type. If the user writes let x: u8 = 8, the type should be kept IMO. However, if the literal has a suffix 8_u8 then the type can be removed. You can take a look at LitKind::is_unsuffixed() for this one. For &str etc it should be fine to remove the type as the literal is clear.

Copy link
Member

@xFrednet xFrednet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally speaking, I think this is a good start, which we could actually merge as a minimum viable product. There are still several examples that could be improved. However, most of them seem a bit complicated to fix and the question is how much benefit we'll get from them. It should be fine as is, since all ToDos are false negatives.

What are your thoughts? Do you want to try perfecting it in this PR or get this merge ready and create follow-ups? (Assuming, you're still interested in the lint)

tests/ui/redundant_type_annotations.rs Outdated Show resolved Hide resolved
tests/ui/redundant_type_annotations.rs Outdated Show resolved Hide resolved

let _return: u32 = new_pie.return_an_int();

let _return: String = String::from("test");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This currently doesn't trigger a lint, even if the function comment says it should. I'm not quite sure why, as String::new() etc are covered.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, I missed that, thanks!

I digged into it a bit, it turns out it is related to generics.
When I do cx.tcx.type_of(func_defid) of the function, I get EarlyBinder(fn(T) -> Self {<Self as std::convert::From<T>>::from})

If I then execute subst_identity or skip_binder on this, I get fn(T) -> Self {<Self as std::convert::From<T>>::from}

Then, when I do func_ty.fn_sig(cx.tcx).output() where func_ty is the type I wrote previously, I get Binder(Self, []), then I interpret the return type as Self instead of String.

Do you know what I could use in this case? I tried having a look at other lints but I was not able to find anything, thanks!

Copy link
Contributor Author

@AlessioC31 AlessioC31 May 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @xFrednet , do you have any clue on this? Thanks!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I missed your question earlier 😅. I'm not totally sure about this one, but you might want to take a look at:

If both of these don't work, I'd probably ask on Zulip the others might have a good idea for this one :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I missed your question earlier 😅. I'm not totally sure about this one, but you might want to take a look at:

If both of these don't work, I'd probably ask on Zulip the others might have a good idea for this one :)

I've tried the two methods you suggested without any luck. I've asked on Zulip, let's see.

Thanks!

@AlessioC31
Copy link
Contributor Author

Generally speaking, I think this is a good start, which we could actually merge as a minimum viable product. There are still several examples that could be improved. However, most of them seem a bit complicated to fix and the question is how much benefit we'll get from them. It should be fine as is, since all ToDos are false negatives.

What are your thoughts? Do you want to try perfecting it in this PR or get this merge ready and create follow-ups? (Assuming, you're still interested in the lint)

Hello @xFrednet , thanks again.

I'm still interested and I plan to perfecting this PR (at least adding support for primitive types and the suggestions you mentioned in the previous reviews).
I had to pause working on this because I had a busy period, it should be better next week!

@AlessioC31
Copy link
Contributor Author

AlessioC31 commented May 16, 2023

Hey @AlessioC31, what is the current status of the PR? Are there still open TODOs, besides the rebase? :)

Hello @xFrednet , there's still the problem with the String::from (and other generics since it's the same I guess). I've asked on Zulip but I haven't received any answer, maybe I can wrap this PR, I'll add support for refs, rebase and then it's ready to being reviewed. It won't lint in case generics are used but maybe I can come back at this when I understand how to do it

@AlessioC31
Copy link
Contributor Author

AlessioC31 commented May 19, 2023

Hello @xFrednet, I think this is ready for review.

We miss a couple of things which I think they could be added in the future, like:

  • Support for generics
  • Refs returned from anything else than a MethodCall (maybe it's because of my little experience with rust yet, but I think this shouldn't happen often, what do you think?)
  • Complex types (tuples, arrays, etc...)
  • Path to something different than a primitive type. Like for now this lint supports cases like let a: u32 = u32::MAX but it doesn't support things like let a: MyStruct = Somewhere::MyStaticInstance. Do you think we need to add this?

Thanks!

@AlessioC31 AlessioC31 force-pushed the redundant_type_annotations branch 2 times, most recently from be50a5f to 1ef6023 Compare May 19, 2023 16:08
@AlessioC31 AlessioC31 requested a review from xFrednet May 20, 2023 08:29
@xFrednet
Copy link
Member

xFrednet commented May 21, 2023

Hello @xFrednet, I think this is ready for review.

We miss a couple of things which I think they could be added in the future, like:

  • Support for generics

As long as we don't have false positives and only false negatives, that's totally fine :). Could you add this limitation, as well as the complex types and path one into the lint documentation? I like to use a ### Limitations section to list such things. After this PR, we should ideally also create issues for these :)

  • Refs returned from anything else than a MethodCall (maybe it's because of my little experience with rust yet, but I think this shouldn't happen often, what do you think?)

We should make sure that normal function calls are also detected. For one, because they're used quite often and because it should be a simple addition. I can mark the required changes in the next review :)

  • Complex types (tuples, arrays, etc...)

Complex types are sadly complex to lint. I've been contemplating, how to best handle lints for these, for a different project. I sadly haven't found a good option. This is to say, that it's fine if these are not caught, probably. I guess that Clippy has quite a few lints that can't handle these :)

  • Path to something different than a primitive type. Like for now this lint supports cases like let a: u32 = u32::MAX but it doesn't support things like let a: MyStruct = Somewhere::MyStaticInstance. Do you think we need to add this?

I believe that should be fine. Such a pattern is rarely used in the projects I'm using. We can leave this as an improvement for the future :)

Thanks!

You're very welcome, thank you for working on Clippy! I'll review this PR in the upcoming week :)

@bors
Copy link
Collaborator

bors commented May 23, 2023

☔ The latest upstream changes (presumably #10810) made this pull request unmergeable. Please resolve the merge conflicts.

Copy link
Member

@xFrednet xFrednet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This version looks good to me. I would say, it only requires the limitations section and a rebase. Then we can merge it! :)

@AlessioC31
Copy link
Contributor Author

I think I've addressed all your comments and rebased to master. Should I squash the commits?

Thanks!

@xFrednet
Copy link
Member

xFrednet commented Jun 2, 2023

I think I've addressed all your comments and rebased to master.

There still seems to be a conflict, could you resolve that one?

Should I squash the commits?

It's not mandatory, but would be nice, 1 - 5 commits would be a good balance :)

I intend to review this over the weekend :)

@AlessioC31 AlessioC31 force-pushed the redundant_type_annotations branch 2 times, most recently from 5d4cee7 to 3df2a43 Compare June 4, 2023 04:47
@AlessioC31
Copy link
Contributor Author

I think I've addressed all your comments and rebased to master.

There still seems to be a conflict, could you resolve that one?

Should I squash the commits?

It's not mandatory, but would be nice, 1 - 5 commits would be a good balance :)

I intend to review this over the weekend :)

Ok I've rebased to master and reduced the amount of commits, thanks!

@AlessioC31
Copy link
Contributor Author

Rebased again :D

@xFrednet
Copy link
Member

xFrednet commented Jun 6, 2023

This version looks good to me. Thank you for all the work you put into this lint! I appreciate it!

@bors r+

@bors
Copy link
Collaborator

bors commented Jun 6, 2023

📌 Commit a9b468f has been approved by xFrednet

It is now in the queue for this repository.

@bors
Copy link
Collaborator

bors commented Jun 6, 2023

⌛ Testing commit a9b468f with merge cc8ead2...

@bors
Copy link
Collaborator

bors commented Jun 6, 2023

☀️ Test successful - checks-action_dev_test, checks-action_remark_test, checks-action_test
Approved by: xFrednet
Pushing cc8ead2 to master...

@bors bors merged commit cc8ead2 into rust-lang:master Jun 6, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-review Status: Awaiting review from the assignee but also interested parties
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Warn on redundant / needless type annotations
4 participants